using System;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using Android.Content;
using Android.Util;
using Java.Lang;
using OpenTK;
using OpenTK.Audio.OpenAL;
using OpenTK.Graphics;
using OpenTK.Graphics.ES11;
using OpenTK.Platform.Android;
using Exception = System.Exception;

namespace MapDrawer
{
    public class Painter : AndroidGameView
    {
        private readonly MainActivity baseActivity;
        private readonly float[][] BuildingsArray;
        private readonly float[][] RoadsArray;
        private readonly byte[][] RoadsColorArray;
        private readonly byte[][] BuildingsColorArray;
        private readonly float[] TripArray;
        private readonly byte[] TripColorArray;
        
        public Painter(Context context, IAttributeSet attrs) :
            base(context, attrs)
        {
            baseActivity = (MainActivity) context;
            baseActivity.RegisterPainter(this);

	        JsonOsm osm = baseActivity.Osm;
			
			//BuildingsArray
	        BuildingsArray = osm.Ways.Where(x => x.Type == WayType.Building).Select(x => x.Nodes.SelectMany(node => new float[] {(float) node.Lon, (float) node.Lat}).ToArray()).ToArray();
			RoadsArray = osm.Ways.Where(x => x.Type == WayType.Road).Select(x => x.Nodes.SelectMany(node => new float[] { (float)node.Lon, (float)node.Lat }).ToArray()).ToArray();

            RoadsColorArray = new byte[RoadsArray.Length][];
            int colorIndex = 0;
            foreach (var road in RoadsArray)
            {
                RoadsColorArray[colorIndex] = new byte[road.Length * 2];
                for (int i = 0; i < road.Length *2;)
                {
                    RoadsColorArray[colorIndex][i++] = 133;
                    RoadsColorArray[colorIndex][i++] = 113;
                    RoadsColorArray[colorIndex][i++] = 106;
                    RoadsColorArray[colorIndex][i++] = 255;
                }
                colorIndex++;
            }


            BuildingsColorArray = new byte[BuildingsArray.Length][];
            colorIndex = 0;
            foreach (var building in BuildingsArray)
            {
                BuildingsColorArray[colorIndex] = new byte[building.Length * 2];
                for (int i = 0; i < building.Length * 2; )
                {
                    BuildingsColorArray[colorIndex][i++] = 103;
                    BuildingsColorArray[colorIndex][i++] = 113;
                    BuildingsColorArray[colorIndex][i++] = 121;
                    BuildingsColorArray[colorIndex][i++] = 255;
                }
                colorIndex++;
            }


            //TripArray 
            if (baseActivity.TripAvailable)
            {
                TripArray = new float[(baseActivity.Trip.Points.Count*2)];
                var tripIndex = 0;
                //TripArray[tripIndex++] = 0;
                //TripArray[tripIndex++] = 0;
                baseActivity.Trip.Points.ForEach(p =>
                {
                    TripArray[tripIndex++] = (float) p.Coordinates.Longitude;
                    TripArray[tripIndex++] = (float) p.Coordinates.Latitude;
                });

                TripColorArray = new byte[TripArray.Length*2];
                for (var i = 0; i < TripColorArray.Length; i += 4)
                {
                    TripColorArray[i] = 255;
                    TripColorArray[i + 1] = 10;
                    TripColorArray[i + 2] = 10;
                    TripColorArray[i + 3] = 255;
                }
            }
        }
        
        void Render()
        {
            Log.Verbose("MapDrawer.Painter", "Render method called.");
            GL.ClearColor(0.96f, 0.84f, 0.61f, 1.0f);
            GL.Clear((uint)All.ColorBufferBit);

            GL.EnableClientState(All.VertexArray);
            GL.EnableClientState(All.ColorArray);
            unsafe
            {
                for (var i = 0; i < BuildingsArray.Length; i++)
                {
                    fixed (float* ptr = BuildingsArray[i])
                    {
                        fixed (byte* ptrCol = BuildingsColorArray[i])
                        {
                        GL.VertexPointer(2, All.Float, 0, new IntPtr(ptr));
                            GL.ColorPointer(4, All.UnsignedByte, 0, new IntPtr(ptrCol));
                        GL.DrawArrays(All.TriangleStrip, 0, BuildingsArray[i].Length/2);
                        }
                    }
                }
                
                for (var i = 0; i < RoadsArray.Length; i++)
                {
                    fixed (float* ptr = RoadsArray[i])
                    {
                        fixed (byte* ptrCol = RoadsColorArray[i])
                        {
                            GL.VertexPointer(2, All.Float, 0, new IntPtr(ptr));
                            GL.ColorPointer(4, All.UnsignedByte, 0, new IntPtr(ptrCol));
                            GL.DrawArrays(All.LineStrip, 0, RoadsArray[i].Length/2);
                        }
                    }
                }
                 
                if (baseActivity.TripAvailable)
                {
                    GL.EnableClientState(All.ColorArray);
                    Log.Debug("Render", "Drawing path");
                    foreach (float f in TripArray)
                    {
                        Log.Debug("Path coordinates", "" + f);
                    }
                    fixed (float* ptr = TripArray)
                    {
                        fixed (byte* ptrCol = TripColorArray)
                        {

                            GL.VertexPointer(2, All.Float, 0, new IntPtr(ptr));
                            GL.ColorPointer(4, All.UnsignedByte, 0, new IntPtr(ptrCol));

                            GL.DrawArrays(All.LineStrip, 0, TripArray.Length/2);
                        }
                    }
            }

        }
            GL.Finish();
            GL.DisableClientState(All.ColorArray);
			GL.DisableClientState(All.VertexArray);

            SwapBuffers();
        }

	    #region not important functions

	    protected override void CreateFrameBuffer()
	    {
		    GLContextVersion = GLContextVersion.Gles1_1;
		    try
		    {
			    base.CreateFrameBuffer();
			    return;
		    }
		    catch (Exception ex)
		    {
			    Log.Debug("FrameBufferError", "Cannot create FrameBuffer with default params: {0}", ex);
		    }
		    try
		    {
			    GraphicsMode = new AndroidGraphicsMode(0, 0, 0, 0, 0, false);
			    base.CreateFrameBuffer();
			    return;
		    }
		    catch (Exception ex)
		    {
			    Log.Debug("FrameBufferError", "Cannot create FrameBuffer with minimum params: {0}", ex);
		    }
		    throw new Exception("Can't load egl, aborting");
	    }

	    public void MoveMap(float x, float y)
	    {
		    GL.Translate(x, y, 0);
		    Render();
	    }

	    public void Zoom(float scaleFactor)
	    {
		    Log.Debug("Painter.Zoom", "zooming with value: " + scaleFactor);
		    GL.Scale(scaleFactor, scaleFactor, scaleFactor);
		    Render();
	    }

	    protected override void OnLoad(EventArgs e)
	    {
		    Log.Debug("MapDrawer.Painter", "OnLoad method called");
		    base.OnLoad(e);
		    if (BuildingsArray == null || RoadsArray == null)
			    throw new Exception("Cannot draw anything arrays are null");
		    RenderFrame += (sender, args) => Render();
		    /*
			GL.Enable(All.CullFace);
			GL.ShadeModel(All.Smooth);
			GL.Hint(All.PerspectiveCorrectionHint, All.Nicest);
			*/
		    // Run the render loop
		    Run(0);
	    }

	    protected override void OnResize(EventArgs e)
	    {
		    Log.Debug("MapDrawer.Painter", "OnResize method called.");
		    //GL.Enable(All.ScissorTest);
		    //GL.Scissor(1, 47, 5, 5);
		    //GL.Disable(All.ScissorTest);
	    }


	    #endregion


        #region basicMovementFunctions
        public void ZoomIn()
        {
            GL.Scale(1.25f, 1.25f, 1.25f);
            Render();
        }

        public void ZoomOut()
        {
            GL.Scale(0.75f, 0.75f, 0.75f);
            Render();
        }

        public void GoLeft()
        {
            GL.Translate(0.025f, 0 , 0);
            Render();
        }

        public void GoRight()
        {
            GL.Translate(-0.025f, 0, 0);
            Render();
        }

        public void GoUp()
        {
            GL.Translate(0, -0.025f, 0);
            Render();
        }


        public void GoDown()
        {
            GL.Translate(0, 0.025f, 0);
            Render();
        }
        #endregion
    }
}